-module(account_rpc).

-export([
    handle/3

    ,apply_fix_link/3
]).

-include("common.hrl").
-include("role_data.hrl").
-include("pb.hrl").
-include("link.hrl").
-include("net.hrl").
-include("role.hrl").

-define(role_online_max, 6000).
-define(hb_interval, 2).


%% 心跳
handle(?p_account_heart, _Bin, State = #net_conn{hb_count = HbCount, hb_start = HbStart, hb_err = HbErr}) ->
    Now = util_time:unixtime(),
    util_net:send(self(), system_api:msg_time(Now)),
    case HbCount >= 2 of
        true ->
            Diff = max(Now - HbStart, 1),
            AccSpeed = ?hb_interval * 2 / Diff,
            case AccSpeed >= 1.4 of
                true ->
                    ?DEBUG("心跳包存在加速：count=~w, start=~w, now = ~w, diff = ~w, acc_speed=~w, err_count=~w", [HbCount, HbStart, Now, Now - HbStart, AccSpeed, HbErr]),
                    case HbErr >= 3 of
                        true ->
                            {stop, "客户端使用了加速软件"};
                        false ->
                            {ok, State#net_conn{hb_last = Now, hb_count = 1, hb_start = Now, hb_err = HbErr + 1}}
                    end;
                false ->
                    {ok, State#net_conn{hb_last = Now, hb_count = 1, hb_start = Now, hb_err = 1}}
            end;
        _ ->
            {ok, State#net_conn{hb_last = Now, hb_count = HbCount + 1}}
    end;

%% 登录
handle(?p_account_login, {Account, _Time, _Sign}, Conn) ->
    util_net:pack_send(self(), ?p_account_login_ok, {}),
    {ok, Conn#net_conn{account = Account}};

%% 获取角色列表
handle(?p_account_roles, {SidReq}, #net_conn{account = Account}) ->
    case role_mgr:get_role_base(by_account, Account) of
        {ok, RoleBases} ->
            L = [{Rid, Name, Sex, Lev} || #role_base{rid = Rid, sid = Sid, name = Name, sex = Sex, lev = Lev} <- RoleBases, SidReq =:= Sid],
            util_net:pack_send(self(), ?p_account_roles_ok, {L}),
            ok;
        {error, Reason} ->
            ?ERR("获取角色列表失败:~w", [Reason]),
            ok
    end;

%% 请求创建角色
handle(?p_account_role_create, {<<>>, _, _}, #net_conn{}) ->
    util_net:send(self(), system_api:msg_error(10086)),
    {error, "创建角色错误：名字为空"};
handle(?p_account_role_create, {_, Sex, _}, #net_conn{})  when Sex =/= ?female andalso Sex =/= ?male ->
    util_net:send(self(), system_api:msg_error(10086)),
    {error, "创建角色错误：性别错误"};
handle(?p_account_role_create, {Sid, Sex, Name}, Conn = #net_conn{account = Account, ip = Ip}) ->
    case role_mgr:create_role(Account, Sid, Name, Sex, Ip) of
        {ok, Rid} ->
            util_net:pack_send(self(), ?p_account_role_create_ok, {Rid}),
            {ok, Conn};
        {error, Reason} ->
            util_net:send(self(), system_api:msg_error(10086)),
            {error, Reason}
    end;

%% 角色登录
handle(?p_account_role_login, _Bin, #net_conn{type = game, object_pid = ObjectPid}) when ObjectPid =/= undefined ->
    {error, "角色已在线，不可重复登入"};
handle(?p_account_role_login, {Rid, ExtParam}, Conn = #net_conn{type = Type, account = Account, socket = Socket, ip = Ip, port = Port})
    when Type == game orelse Type == tester ->
    case role_online_mgr:size() >= ?role_online_max of
        true ->
            {error, "服务器繁忙，请稍候再试"};
        false ->
            Link = #link{socket = Socket, conn_pid = self(), ip = Ip, port = Port},
            Conn2 = Conn#net_conn{object_id = Rid},

            case role_api:lookup(by_id, Rid, [#role.pid, #role.account]) of
                {error, not_found} ->
                    role_login(Rid, Link, Conn2, ExtParam);
                {ok, _Node, [Pid, Account]} ->
                    role_switch(Pid, Link, Conn2, ExtParam);
                {error, Error} ->
                    ?ERR("角色[~w]登录失败:~w", [Rid, Error]),
                    {error, "您的帐号和角色不匹配"}
            end
    end;

handle(_Code, _Cmd, _Conn) ->
    {error, unknown_cmd}.


%%%===================================================================
%%% Internal functions
%%%===================================================================

role_login(Rid, Link, Conn = #net_conn{is_cowboy = IsCowboy, account = Account, type = CliType}, ExtParam) ->
    Sid = util:get_sid(Rid),
    case role:create(Rid, Sid, Account, Link, ExtParam, CliType, IsCowboy) of
        {ok, Pid} ->
            erlang:link(Pid),
            {ok, Conn#net_conn{object = role, object_pid = Pid, object_id = Rid}};
        {error, {already_started, _}} ->
            ?ERR("角色[Rid:~w]登录失败，原因: 该角色已经在线", [Rid]),
            {error, "角色已在线，不可重复登入"};
        {error, Err} when is_list(Err) ->
            ?ERR("加载角色数据时发生异常: ~s", [Err]),
            {error, util_io:fbin(<<"登入角色失败: ~s">>, [Err])};
        {error, Err} when is_binary(Err) ->
            ?ERR("加载角色数据时发生异常: ~s", [Err]),
            {error, util_io:fbin(<<"登入角色失败: ~s">>, [Err])};
        {error, Err} ->
            ?ERR("加载角色数据时发生异常: ~p", [Err]),
            {error, util_io:fbin(<<"登入角色失败: ~w">>, [Err])}
    end.

role_switch(Pid, Link, Conn, ExtParam) ->
    case role:apply(sync, Pid, {?MODULE, apply_fix_link, [Link, ExtParam]}) of
        true -> {ok, Conn#net_conn{object = role, object_pid = Pid}};
        _ -> {false, 0}
    end.

%% 替换角色梆定的连接器(用户顶号操作)
apply_fix_link(Role = #role{link = #link{conn_pid = ConnPid}}, Link, _ExtParam) ->
    NewRole = Role#role{link = Link},

    role_online_mgr:leave(Role),
    role_online_mgr:join(NewRole),

    erlang:unlink(ConnPid),
    erlang:link(Link#link.conn_pid),
    put(conn_pid, Link#link.conn_pid),

    ?IF(get(is_cowboy) =:= true, put(relink, true), ok),

    spawn(
        fun() ->
            util_net:send(ConnPid, system_api:msg_disconnect(10087)),
            util_proc:sleep(500),
            erlang:exit(ConnPid, kill)
        end
    ),

    {ok, true, NewRole}.
